package tomasulo;
import java.util.HashSet;

class Memory { // 注意：放入站中的LD和ST请求会乱序执行！
	static final int LOAD_SIZE = 3;
	static final int STORE_SIZE = 3;
	
	static final int LOAD_CLICK = 2;
	static final int STORE_CLICK = 2;
	
	static final int LD_START_STATIONNO = 1;
	static final int MAX_ADDR = 4098;
	
	static final int AVAILABLE = 0;
	static final int ST_WAITING = -1; // 等待站号对应值到位
	static final int ST_READY = -2;
	static final int STORING = -3;
	static final int LD_READY = 2;
	static final int LOADING = 3;
	
	static final String storeStateName[]={"AVAILABLE","WAITING","READY","STORING"};
	static final String loadStateName[]={"AVAILABLE","","READY","LOADING"};
	
	double val[];
	int readingC[]; // 在读计数器
	int writingI[]; // 在写下标
	
	int ldSize; // 项数
	int ldCtrl[]; // 该项状态
	int ldAddr[]; // 待读取内存地址
	int ldClicks[]; // 读取所耗周期数
	int ldEnd[]; // 读取结束周期（LOADING才有效）
	int ldIdx[];
	
	int stSize;
	int stCtrl[]; // 该项状态
	int stAddr[]; // 目标内存地址
	double stVal[];
	int stStationNo[]; // 期待的站号
	int stClicks[]; // 存储所耗周期数
	int stEnd[]; // 存储结束周期（STORING才有效）
	int stIdx[];
	
	HashSet<Integer> idxDone;
	
	boolean finished(int idx) {
		return idxDone.contains(idx);
	}
	
	String instrState(int idx) {
		if (finished(idx)) {
			return "Finished";
		} else {
			for (int i=0;i<ldSize;i++)
				if (ldCtrl[i]!=AVAILABLE && ldIdx[i]==idx) {
					switch (ldCtrl[i]) {
					case LD_READY:
						return "Load Ready";
					case LOADING:
						return "Loading";
					}
				}
			for (int i=0;i<stSize;i++)
				if (stCtrl[i]!=AVAILABLE && stIdx[i]==idx) {
					switch (ldCtrl[i]) {
					case ST_WAITING:
						return "Store Waiting";
					case ST_READY:
						return "Store Ready";
					case STORING:
						return "Storing";
					}
				}
		}
		return "";
	}
	
	int load(int addr, int idx) { // 请求读取MEM[addr]，如果无空项则返回-1，否则返回stationNo
		for (int i = 0; i < ldSize; i++)
			if (ldCtrl[i] == AVAILABLE) {
				ldCtrl[i] = LD_READY;
				ldAddr[i] = addr;
				ldClicks[i] = 0;
				ldIdx[i] = idx;
				return i+LD_START_STATIONNO;
			}
		return -1;
	}
	
	int store(int stationNo, int addr, int idx) { // 如果无空项或者有尚未开始的写同一addr操作，则返回-1；返回值非负表示成功添加
		int ret = -1;
		for (int i = 0; i < stSize; i++)
			if (ret==-1 && stCtrl[i]==AVAILABLE) {
				stCtrl[i] = ST_WAITING;
				stAddr[i] = addr;
				stStationNo[i] = stationNo;
				stClicks[i] = 0;
				stIdx[i] = idx;
				ret = i;
			} else if (stCtrl[i]==ST_WAITING && stAddr[i]==addr) {
				return -1;
			}
		return ret;
	}

	int store(double val, int addr, int idx) {
		int ret = -1;
		for (int i = 0; i < stSize; i++)
			if (ret==-1 && stCtrl[i]==AVAILABLE) {
				stCtrl[i] = ST_READY;
				stAddr[i] = addr;
				stVal[i] = val;
				stStationNo[i] = -1;
				stClicks[i] = 0;
				stIdx[i] = idx;
				ret = i;
			} else if (stCtrl[i]==ST_WAITING && stAddr[i]==addr) {
				return -1;
			}
		return ret;
	}
	
	public void dispatch(int stationNo, double val) {
		System.out.println("[Memory.dispatch]");
		for (int i = 0; i < stSize; i++)
			if (stCtrl[i]==ST_WAITING && stStationNo[i]==stationNo) {
				stCtrl[i] = ST_READY;
				stStationNo[i] = -1;
				stVal[i] = val;
			}
	}
	
	void clear() {
		for (int i=0;i<=MAX_ADDR;i++) {
			val[i] = 0;
			readingC[i] = 0;
			writingI[i] = -1;
		}
		for (int i = 0; i < ldSize; i++)
			ldCtrl[i] = AVAILABLE;
		for (int i = 0; i < stSize; i++)
			stCtrl[i] = AVAILABLE;
			
		idxDone.clear();
	}
	
	void click() {
		System.out.println("[Memory.click]");
		
		for (int i = 0; i < ldSize; i++) {
			switch (ldCtrl[i]) {
			case LD_READY:
				if (writingI[ldAddr[i]] == -1) { // 待读内存地址没有写操作
					readingC[ldAddr[i]]++;
					ldEnd[i] = ldClicks[i]+LOAD_CLICK;
					ldCtrl[i] = LOADING;
				}
				break;
			case LOADING:
				if (ldClicks[i]+1 == ldEnd[i]) { // 本周期结束load完成
					readingC[ldAddr[i]]--;
					Kernel.kernel.dispatch(i+LD_START_STATIONNO, val[ldAddr[i]]);
					idxDone.add(ldIdx[i]);
					ldCtrl[i] = AVAILABLE;
                                        Kernel.kernel.manager.setFinishTime(ldIdx[i], Kernel.kernel.cycle);
					Kernel.kernel.manager.setState(ldIdx[i], Instruction.FINISHED);
				}
				break;
			}
			ldClicks[i]++;
		}
		
		for (int i = 0; i < stSize; i++) {
			switch (stCtrl[i]) {
			case ST_READY:
				if (writingI[ldAddr[i]]==-1 && readingC[ldAddr[i]]==0) { // 待写内存地址没有读、写操作
					writingI[ldAddr[i]] = i;
					stEnd[i] = stClicks[i]+STORE_CLICK;
					stCtrl[i] = STORING;
				}
				break;
			case STORING:
				if (stClicks[i]+1 == stEnd[i]) { // 本周期结束store完成
					writingI[stAddr[i]] = -1;
					val[stAddr[i]] = stVal[i];
					idxDone.add(stIdx[i]);
					stCtrl[i] = AVAILABLE;
					Kernel.kernel.manager.setFinishTime(stIdx[i], Kernel.kernel.cycle);
					Kernel.kernel.manager.setState(stIdx[i], Instruction.FINISHED);
				}
				break;
			}
			stClicks[i]++;
		}
	}
	
	
	String[][] loadQueue(){
		String[][] ret = new String[3][2]; // State, getAddr
		for (int i=0;i<3;i++) {
			//ret[i][0]=new String("Station "+(LD_START_STATIONNO+i));
			switch (ldCtrl[i]) {
			case AVAILABLE:
				ret[i][0]="Available";
				ret[i][1]="";
				break;
			case LD_READY:
				ret[i][0]="Ready";
				ret[i][1]=(new Integer(ldAddr[i])).toString();
				break;
			case LOADING:
				ret[i][0]="Loading";
				ret[i][1]=(new Integer(ldAddr[i])).toString();
				break;
			}
		}
		return ret;
	}
	
	String[][] storeQueue(){
		String[][] ret=new String[3][4]; // State, setAddr, setValue, reqStationNo
		for (int i=0;i<3;i++)
			switch (ldCtrl[i]) {
			case AVAILABLE:
				ret[i][0]="Available";
				ret[i][1]="";
				ret[i][2]="";
				ret[i][3]="";
				break;
			case ST_WAITING:
				ret[i][0]="Store Waiting";
				ret[i][1]=(new Integer(stAddr[i])).toString();
				ret[i][2]="";
				ret[i][3]=(new Integer(stStationNo[i])).toString();
				break;
			case ST_READY:
				ret[i][0]="Store Ready";
				ret[i][1]=(new Integer(stAddr[i])).toString();
				ret[i][2]=String.valueOf((stVal[i]));
				ret[i][3]="";
				break;
			case STORING:
				ret[i][0]="Storing";
				ret[i][1]=(new Integer(stAddr[i])).toString();
				ret[i][2]=String.valueOf((stVal[i]));
				ret[i][3]="";
				break;
			}
		return ret;
	}
	
	Memory() {
		val = new double[MAX_ADDR+1];
		readingC = new int[MAX_ADDR+1];
		writingI = new int[MAX_ADDR+1];
		
		ldSize = LOAD_SIZE;
		ldCtrl = new int[ldSize];
		ldAddr = new int[ldSize];
		ldClicks = new int[ldSize];
		ldEnd = new int[ldSize];
		ldIdx = new int[ldSize];
		
		stSize = STORE_SIZE;
		stCtrl = new int[stSize];
		stAddr = new int[stSize];
		stVal = new double[stSize];
		stStationNo = new int [stSize];
		stClicks = new int [stSize];
		stEnd = new int[stSize];
		stIdx = new int[stSize];
		
		idxDone = new HashSet<Integer>();
		
		clear();
	}
}